package dotaframework.game;

import java.awt.Canvas;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import dota.ImageManager;
import dota.entity.ArmedUnit;
import dota.entity.ArmedUnitSoldier;
import dota.entity.ArmedUnitSquad;
import dota.entity.Castle;
import dota.entity.Path;
import dota.entity.Wall;
import dota.rules.DotaMoveBlockerChecker;
import dota.rules.DotaMoveStrategyKeyboard;
import dotaframework.utils.AgeFactory;
import dotaframework.utils.MiddleAgeFactory;
import gameframework.base.MoveStrategy;
import gameframework.base.ObservableValue;
import gameframework.game.CanvasDefaultImpl;
import gameframework.game.Game;
import gameframework.game.GameLevelDefaultImpl;
import gameframework.game.GameMovableDriver;
import gameframework.game.GameMovableDriverDefaultImpl;
import gameframework.game.GameUniverse;
import gameframework.game.GameUniverseViewPortDefaultImpl;
import gameframework.game.MoveBlockerChecker;
import gameframework.game.MoveBlockerRulesApplierDefaultImpl;
import gameframework.game.OverlapProcessor;
import gameframework.game.OverlapProcessorDefaultImpl;
import gameframework.game.OverlapRulesApplier;
import gameframework.game.OverlapRulesApplierDefaultImpl;

public class DotaGameLevel extends GameLevelDefaultImpl {

	Canvas canvas;
	// 0 : Path; 1 : Walls; 2 : CastleBlue 3 : CastleRed; 4 : Castle Overlap;
		static int[][] tab = {
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},

	};

		public static final int SPRITE_SIZE = 16;
		public static final int NUMBER_OF_ARMY = 3;
		public static final int NUMBER_OF_MEN_BLUE = 6;
		public static final int NUMBER_OF_MEN_RED = 7;
		private AgeFactory spawner;
		private ArmedUnit teamBlue;
		private ArmedUnit teamRed;
		private MoveBlockerChecker moveBlockerChecker;
		
		protected ObservableValue<Integer> experience[];
		protected ObservableValue<Integer> heroLevel[];
		protected ObservableValue<Integer> gold[];
		
		protected Game game;
		
	public DotaGameLevel(Game g) {
		super(g);
		game = g;
		
		this.experience = ((DotaGameImpl)g).experience;
		this.heroLevel = ((DotaGameImpl)g).heroLevel;
		this.gold = ((DotaGameImpl)g).gold;
		
		canvas = g.getCanvas();
		ImageManager.loadImage(canvas);
	}

	@Override
	protected void init() {
		OverlapProcessor overlapProcessor = new OverlapProcessorDefaultImpl();

		moveBlockerChecker = new DotaMoveBlockerChecker();
		moveBlockerChecker.setMoveBlockerRules(new 	MoveBlockerRulesApplierDefaultImpl());
		
		OverlapRulesApplier overlapRules = new OverlapRulesApplierDefaultImpl() {
			
			@Override
			public void setUniverse(GameUniverse universe) {
				// TODO Auto-generated method stub
				
			}
		};
		//PacmanOverlapRules overlapRules = new PacmanOverlapRules(new Point(14 * SPRITE_SIZE, 17 * SPRITE_SIZE),
		//		new Point(14 * SPRITE_SIZE, 15 * SPRITE_SIZE), life[0], score[0], endOfGame);
		//overlapProcessor.setOverlapRules(overlapRules);

		universe = new DotaGameUniverse(moveBlockerChecker, overlapProcessor, experience[0], heroLevel[0], gold[0]);
		overlapRules.setUniverse(universe);

		gameBoard = new GameUniverseViewPortDefaultImpl(canvas, universe);
		((CanvasDefaultImpl) canvas).setDrawingGameBoard(gameBoard);
		
		// Filling up the universe with basic non movable entities and inclusion in the universe
				for (int i = 0; i < DotaGameImpl.NB_ROWS; ++i) {
					for (int j = 0; j < DotaGameImpl.NB_COLUMNS; ++j) {
						if (tab[i][j] == 0) {
							universe.addGameEntity(new Path(j * SPRITE_SIZE, i * SPRITE_SIZE));
						}
						if (tab[i][j] == 1) {
							universe.addGameEntity(new Wall(j * SPRITE_SIZE, i * SPRITE_SIZE));
						}
						if (tab[i][j] == 2) {
							universe.addGameEntity(new Castle(j * SPRITE_SIZE, i * SPRITE_SIZE, "teamBlue-Castle"));
						}
						if (tab[i][j] == 3) {
							universe.addGameEntity(new Castle(j * SPRITE_SIZE, i * SPRITE_SIZE, "teamRed-Castle"));
						}
					}
				}
				
				spawner = new MiddleAgeFactory();
				
				//Team 1 definition
				teamBlue = new ArmedUnitSquad(spawner, "teamBlue");
				
				//Player definition and inclusion in the universe
				
				createHero();

				teamRed = new ArmedUnitSquad(spawner, "teamRed");
				
				createSoldiers();
	}
	
	public void createSoldiers(){
		// Infantry definition and inclusion in the universe
		for(int i = 0; i < NUMBER_OF_ARMY; ++i) {
			ArmedUnit army = new ArmedUnitSquad(spawner, "Army"+i);
			
			for(int j = 0; j < NUMBER_OF_MEN_BLUE; ++j) {
			
				ArmedUnit soldier = new ArmedUnitSoldier(spawner, "Simple", "teamBlue"+j+"-"+i, 
						ImageManager.SOLDIER1);
				((ArmedUnitSquad) army).addUnit(soldier);
				
				GameMovableDriver soldierDriv = new DotaGameMovableDriver();
				Point startPoint = new Point(7 * i * SPRITE_SIZE + 170 +j*SPRITE_SIZE, 5 * SPRITE_SIZE);
				
				List<Point> goals = new ArrayList<Point>();
				switch(i)
				{
				case 0:
					goals.add(new Point((4 + (int)(Math.random() * 5)) * SPRITE_SIZE, (3 + (int)(Math.random() * 4)) * SPRITE_SIZE));
					goals.add(new Point((4 + (int)(Math.random() * 5)) * SPRITE_SIZE, (34 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					goals.add(new Point(20 * SPRITE_SIZE, (34 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					break;
				case 1:
					goals.add(new Point(325, 520));
					break;
				case 2:
					goals.add(new Point((33 + (int)(Math.random() * 5)) * SPRITE_SIZE, (3 + (int)(Math.random() * 4)) * SPRITE_SIZE));
					goals.add(new Point((33 + (int)(Math.random() * 5)) * SPRITE_SIZE, (34 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					goals.add(new Point(21 * SPRITE_SIZE, (34 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					break;
				}
				
				MoveStrategy goalsStr = new MoveStrategyMultipleGoal(startPoint, goals);
				((DotaGameMovableDriver)soldierDriv).setStrategy(goalsStr);
				((DotaGameMovableDriver)soldierDriv).setmoveBlockerChecker(moveBlockerChecker);
				((ArmedUnitSoldier)soldier).setDriver(soldierDriv);
				((ArmedUnitSoldier)soldier).setPosition(startPoint);
				universe.addGameEntity((ArmedUnitSoldier)soldier);
			}
			((ArmedUnitSquad)teamBlue).addUnit(army);
			universe.addGameEntity((ArmedUnitSquad)army);
		}
		
		//Team2 definition
		
		
		// Infantry definition and inclusion in the universe
		for(int i = 0; i < NUMBER_OF_ARMY; ++i) {
			ArmedUnit army = new ArmedUnitSquad(spawner, "Army"+i);
			
			for(int j = 0; j < NUMBER_OF_MEN_RED; ++j) {
			
				ArmedUnit soldier = new ArmedUnitSoldier(spawner, "Simple", "teamRed"+j+"-"+i, 
						ImageManager.SOLDIER2);
				((ArmedUnitSquad) army).addUnit(soldier);
				
				GameMovableDriver soldierDriv = new DotaGameMovableDriver();
				Point startPoint = new Point(7 * i * SPRITE_SIZE + 170 +j*SPRITE_SIZE, 32 * SPRITE_SIZE);
				
				List<Point> goals = new ArrayList<Point>();
				switch(i)
				{
				case 0:
					goals.add(new Point((4 + (int)(Math.random() * 5)) * SPRITE_SIZE, (32 + (int)(Math.random() * 4)) * SPRITE_SIZE));
					goals.add(new Point((4 + (int)(Math.random() * 5)) * SPRITE_SIZE, (3 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					goals.add(new Point(20 * SPRITE_SIZE, (3 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					
					break;
				case 1:
					goals.add(new Point(325, 40));
					break;
				case 2:
					goals.add(new Point((33 + (int)(Math.random() * 5)) * SPRITE_SIZE, (32 + (int)(Math.random() * 4)) * SPRITE_SIZE));
					goals.add(new Point((33 + (int)(Math.random() * 5)) * SPRITE_SIZE, (3 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					goals.add(new Point(21 * SPRITE_SIZE, (3 + (int)(Math.random() * 2)) * SPRITE_SIZE));
					break;
				}
				
				MoveStrategy goalsStr = new MoveStrategyMultipleGoal(startPoint, goals);
				((DotaGameMovableDriver)soldierDriv).setStrategy(goalsStr);
				((DotaGameMovableDriver)soldierDriv).setmoveBlockerChecker(moveBlockerChecker);
				((ArmedUnitSoldier)soldier).setDriver(soldierDriv);
				((ArmedUnitSoldier)soldier).setPosition(startPoint);
				universe.addGameEntity((ArmedUnitSoldier)soldier);
			}
			((ArmedUnitSquad)teamRed).addUnit(army);
			universe.addGameEntity((ArmedUnitSquad)army);
		}
	}
	
	public void createHero() {
		ArmedUnit hero = new ArmedUnitSoldier(spawner, "Complex", "teamBlue-Hero", ImageManager.HERO1);
		GameMovableDriverDefaultImpl heroDriver = new DotaGameMovableDriver();
		DotaMoveStrategyKeyboard keyStr = new DotaMoveStrategyKeyboard(hero, gold[0]);
		keyStr.getSpeedVector().setSpeed(8);
		heroDriver.setStrategy(keyStr);
		heroDriver.setmoveBlockerChecker(moveBlockerChecker);
		canvas.addKeyListener(keyStr);
		((ArmedUnitSoldier)hero).setDriver(heroDriver);
		((ArmedUnitSoldier)hero).setPosition(new Point(280, 30));
		universe.addGameEntity((ArmedUnitSoldier)hero);
		
		((ArmedUnitSquad)teamBlue).addUnit(hero);
		((DotaGameImpl)g).setHero(hero);
	}
	
	@Override
	public void run() {
		stopGameLoop = false;
		// main game loop :
		long start;
		long reSpawn = new Date().getTime();
		
		while (!stopGameLoop && !this.isInterrupted()) {
			start = new Date().getTime();
			gameBoard.paint();
			universe.allOneStepMoves();
			((DotaGameUniverse)universe).allAttackOnce();

			stopGameLoop = ((DotaGameUniverse)universe).checkEndGame();
			if(new Date().getTime() - reSpawn > 30000){
				createSoldiers();
				if(!((DotaGameUniverse)universe).isHeroAlive()){
					createHero();
				}
				reSpawn = new Date().getTime();
			}
			try {
				long sleepTime = MINIMUM_DELAY_BETWEEN_GAME_CYCLES
						- (new Date().getTime() - start);
				if (sleepTime > 0) {
					Thread.sleep(sleepTime);
				}
			} catch (Exception e) {
			}
		}
		System.out.println("Fin de la partie");
	}

}
